home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / components / flockLivejournalService.js < prev    next >
Text File  |  2007-10-12  |  38KB  |  1,151 lines

  1. // vim: ts=2 sw=2 expandtab cindent
  2. //
  3. // BEGIN FLOCK GPL
  4. // 
  5. // Copyright Flock Inc. 2005-2007
  6. // http://flock.com
  7. // 
  8. // This file may be used under the terms of of the
  9. // GNU General Public License Version 2 or later (the "GPL"),
  10. // http://www.gnu.org/licenses/gpl.html
  11. // 
  12. // Software distributed under the License is distributed on an "AS IS" basis,
  13. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. // for the specific language governing rights and limitations under the
  15. // License.
  16. // 
  17. // END FLOCK GPL
  18. //
  19.  
  20. // CONTENTS OF THIS FILE:
  21. // This file contains the implementation of the LiveJournal service. This
  22. // involves the following components:
  23. //   flockLJService - the service class itself
  24. //   flockLJController - the controller class
  25. //   flockLJServiceFactory - factory object for flockLJService
  26. //   flockLJModule - module object for XPCOM registration
  27. //   flockLJAccount - the account class
  28.  
  29. const ENABLE_DEBUG = true; // switch to turn off slow debug code for production
  30. function DEBUG(x) { if (ENABLE_DEBUG) debug("flockLivejournalService: "+x+"\n"); }
  31.  
  32. const Cc = Components.classes;
  33. const Ci = Components.interfaces;
  34. const Cr = Components.results;
  35. const Cu = Components.utils;
  36.  
  37. const LIVEJOURNAL_CID        = Components.ID("{e642271b-1b0d-4d5d-ab51-34de23f75fc7}");
  38. const LIVEJOURNAL_CONTRACTID = "@flock.com/people/livejournal;1";
  39. const LIVEJOURNAL_TITLE      = "LiveJournal Web Service";
  40. const LIVEJOURNAL_FAVICON    = "http://www.livejournal.com/favicon.ico";
  41. const SERVICE_ENABLED_PREF          = "flock.service.livejournal.enabled";
  42. const CATEGORY_COMPONENT_NAME       = "LiveJournal JS Component"
  43. const CATEGORY_ENTRY_NAME           = "livejournal"
  44.  
  45. const RDFS  = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
  46. const IOS   = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  47. const RDFCU = Cc["@mozilla.org/rdf/container-utils;1"].getService(Ci.nsIRDFContainerUtils);
  48.  
  49. const OBS_TOPIC_FLOCKDOCREADY  = "FlockDocumentReady";
  50. const OBS_TOPIC_XPCOMSHUTDOWN  = "xpcom-shutdown";
  51.  
  52. const FLOCK_NS = "http://flock.com/rdf#";
  53. const RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
  54. const RDF_TYPE = RDFS.GetResource(RDF_NS+"type");
  55. const COOP_TYPE = RDFS.GetResource(FLOCK_NS+"CoopType");
  56.  
  57. const COOP_CHROME_URL = "chrome://browser/content/flock/common/coop.js";
  58. const COOP_FOAFTYPES_CHROME_URL = "chrome://browser/content/flock/common/foaf-coop.js";
  59.  
  60. var gCompTK;
  61. function getCompTK() {
  62.   if (!gCompTK) {
  63.     gCompTK = Components.classes["@flock.com/singleton;1"]
  64.                         .getService(Components.interfaces.flockISingleton)
  65.                         .getSingleton("chrome://browser/content/flock/services/common/load-compTK.js")
  66.                         .wrappedJSObject;
  67.   }
  68.   return gCompTK;
  69. }
  70.  
  71. var gTimers = [];  // For use with the scheduler
  72.  
  73. function loadSubScript(spec) {
  74.   var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
  75.   var context = {};
  76.   loader.loadSubScript(spec, context);
  77.   return context;
  78. }
  79.  
  80. var Coop = loadSubScript(COOP_CHROME_URL).Coop;
  81.  
  82. var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader);
  83. loader.loadSubScript("chrome://browser/content/utilityOverlay.js");
  84. loader.loadSubScript("chrome://browser/content/flock/xmlrpc/xmlrpchelper.js");
  85. loader.loadSubScript("chrome://browser/content/flock/blog/blogBackendLib.js");
  86.  
  87.  
  88. // String defaults... may be updated later through Web Detective
  89. var gStrings = {
  90.   "domains": "livejournal.com",
  91.   "homepage": "http://www.livejournal.com/",
  92.   "userlogin": "http://www.livejournal.com/",
  93.   "userprofile": "http://%accountid%.livejournal.com/profile",
  94.   "editprofile": "http://www.livejournal.com/manage/profile/",
  95.   "userblog": "http://%accountid%.livejournal.com/",
  96.   "userfriends": "http://%accountid%.livejournal.com/friends",
  97.   "userphotos": "http://pics.livejournal.com/%accountid%",
  98.   "userpics": "http://www.livejournal.com/allpics.bml?user=%accountid%",
  99.   "postcomment": "http://fixme.com",
  100.   "userfoaf": "http://www.livejournal.com/users/%accountid%/data/foaf"
  101. };
  102.  
  103. // Helper function for LJ URLs - needs to be updated periodically
  104. function makeLiveJournalURL(aType, aFriendID) {
  105.   switch (aType) {
  106.     case "openProfile":
  107.       return gStrings["userprofile"].replace("%accountid%", aFriendID);
  108.     case "openBlog":
  109.       return gStrings["userblog"].replace("%accountid%", aFriendID);
  110.     case "viewFriends":
  111.       return gStrings["userfriends"].replace("%accountid%", aFriendID);
  112.     case "postComment":
  113.       return gStrings["postcomment"].replace("%accountid%", aFriendID);
  114.     case "openPhotos":
  115.       return gStrings["userphotos"].replace("%accountid%", aFriendID);
  116.     case "userpics":
  117.       return gStrings["userpics"].replace("%accountid%", aFriendID);
  118.     default:
  119.       return gStrings[aType].replace("%accountid%", aFriendID);
  120.   }
  121. }
  122.  
  123. function userBlogsListener(aListener) {
  124.   this.listener = aListener;
  125. }
  126.  
  127. userBlogsListener.prototype = {
  128.   // aResult is an Array (simpleEnumerator) of struct objects
  129.   onResult: function UBL_onResult(aResult) {
  130.     var result = new Array();
  131.     for (i=0; i<aResult.length; i++){
  132.       var entry = aResult[i];
  133.       var newAccount = new BlogAccount(entry.blogName);
  134.       newAccount.blogid = entry.blogid;
  135.       newAccount.apiLink = "";
  136.       newAccount.URL = entry.url;
  137.       result.push(newAccount);
  138.     }
  139.     this.listener.onResult(new simpleEnumerator(result));
  140.   },
  141.   onError: function UBL_onError(aErrorCode, aErrorMsg) {
  142.     // Problem contacting the server, or internal server error
  143.     var error = Cc["@flock.com/error;1"].createInstance(Ci.flockIError);
  144.     error.serviceErrorCode = aErrorCode;
  145.     error.serviceErrorString = aErrorMsg;
  146.     switch (aErrorCode) {
  147.       case 0:
  148.         // Invalid login/pass
  149.         error.errorCode = Ci.flockIError.BLOG_INVALID_AUTH;
  150.         break;
  151.       default:
  152.         // Unknown error code
  153.         error.errorCode = Ci.flockIError.BLOG_UNKNOWN;
  154.     }
  155.     this.listener.onError(null, "", error);
  156.   },
  157.   onFault: function UBL_onFault(aErrorCode, aErrorMsg) {
  158.     // Server said we sent a bad query (e.g. bad auth)
  159.     var error = Cc["@flock.com/error;1"].createInstance(Ci.flockIError);
  160.     error.serviceErrorCode = aErrorCode;
  161.     error.serviceErrorString = aErrorMsg;
  162.     switch (aErrorCode) {
  163.       case 0:
  164.         // Invalid login/pass
  165.         error.errorCode = Ci.flockIError.BLOG_INVALID_AUTH;
  166.         break;
  167.       default:
  168.         // Unknown error code
  169.         error.errorCode = Ci.flockIError.BLOG_UNKNOWN;
  170.     }
  171.     this.listener.onError(null, "", error);
  172.   }
  173. }
  174.  
  175.  
  176. // ================================================
  177. // ========== BEGIN flockLJService class ==========
  178. // ================================================
  179.  
  180. function flockLJService()
  181. {
  182.   /* Constructors of Adapter Services should ensure that they're
  183.   properly represented in the RDF.
  184.   Question: Should the Services all be children of a Services Root?
  185.  
  186.   TODO: Generalize this and make it declarative for every service
  187.   TODO: Add proper refresh intervals to all the object constructors,
  188.   */
  189.  
  190.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  191.                            .getService(Components.interfaces.flockIAccountUtils);
  192.  
  193.   this.obs = Components.classes["@mozilla.org/observer-service;1"]
  194.                        .getService(Components.interfaces.nsIObserverService);
  195.   this.obs.addObserver(this, OBS_TOPIC_XPCOMSHUTDOWN, false);
  196.  
  197.   this.status = Components.interfaces.flockIWebService.STATUS_UNKNOWN;
  198.   this.url = "http://www.livejournal.com";
  199.   this.initialized = false;
  200.  
  201.   // flockIBlogWebService
  202.   this.supportsCategories = 0;
  203.   this.supportsPostReplace = true;
  204.   this.metadataOverlay = "chrome://browser/content/flock/services/livejournal/ljBlogOverlay.xul";
  205.  
  206.   this._ctk = {
  207.     interfaces: [
  208.       "nsISupports",
  209.       "nsISupportsCString",
  210.       "nsIClassInfo",
  211.       "nsIObserver",
  212.       "flockIWebService",
  213.       "flockIBlogWebService",
  214.       "flockISocialWebService",
  215.       "flockIManageableWebService",
  216.       "flockIPollingService",
  217.       //"flockIPeopleActionController",
  218.     ],
  219.     shortName: "livejournal",
  220.     fullName: "LiveJournal",
  221.     description: LIVEJOURNAL_TITLE,
  222.     favicon: LIVEJOURNAL_FAVICON,
  223.     CID: LIVEJOURNAL_CID,
  224.     contractID: LIVEJOURNAL_CONTRACTID,
  225.     accountClass: flockLJAccount
  226.   };
  227.   this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
  228.  
  229.   this.init();
  230. }
  231.  
  232.  
  233. // BEGIN nsIObserver interface
  234. flockLJService.prototype.observe =
  235. function flockLJService_observe(aSubject, aTopic, aState)
  236. {
  237.   switch (aTopic) {
  238.     case OBS_TOPIC_FLOCKDOCREADY:
  239.       var ios = Components.classes["@mozilla.org/network/io-service;1"]
  240.                           .getService(Components.interfaces.nsIIOService);
  241.       var uri = ios.newURI(aState, null, null);
  242.       if (uri.host.indexOf("livejournal.com") > -1) {
  243.         //this.decorateForPerson(aSubject);
  244.       }
  245.       break;
  246.  
  247.     case OBS_TOPIC_XPCOMSHUTDOWN:
  248.       //this.obs.removeObserver(this, OBS_TOPIC_FLOCKDOCREADY);
  249.       this.obs.removeObserver(this, OBS_TOPIC_XPCOMSHUTDOWN);
  250.       break;
  251.   } // switch
  252. }
  253. // END nsIObserver interface
  254.  
  255.  
  256. flockLJService.prototype.loadFoaf =
  257. function flockLJService_loadFoaf(url, aListener) {
  258.   var parser = Components.classes["@mozilla.org/rdf/xml-parser;1"]
  259.                          .createInstance(Components.interfaces.nsIRDFXMLParser);
  260.  
  261.   var hr = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
  262.                      .createInstance(Components.interfaces.nsIXMLHttpRequest);
  263.   var ljServ = this;
  264.  
  265.   var onReadyStateFunc = function(eEvt) {
  266.     if(hr.readyState == 4) {
  267.       var ds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"]
  268.                         .createInstance(Components.interfaces.nsIRDFDataSource);
  269.       parser.parseString(ds, IOS.newURI(url, "UTF-8", null), hr.responseText);
  270.       ljServ.coopify(ds);
  271.       aListener.onResult(ds);
  272.     }
  273.   };
  274.  
  275.   hr.onreadystatechange = onReadyStateFunc;
  276.   hr.detachLoadGroup = true;
  277.   hr.open("GET", url,true);
  278.   hr.overrideMimeType("text/txt");
  279.   hr.send(null);
  280. }
  281.  
  282. flockLJService.prototype.coopify =
  283. function flockLJService_coopify(ds)
  284. {
  285.   var resources = ds.GetAllResources();
  286.   while (resources.hasMoreElements()) {
  287.     var resource = resources.getNext();
  288.     var type_target = ds.GetTarget(resource, RDF_TYPE, true);
  289.     if (type_target) {
  290.       ds.Assert(resource, COOP_TYPE, type_target, true);
  291.     }
  292.   }
  293. }
  294.  
  295. // BEGIN flockIWebService interface
  296. flockLJService.prototype.addAccountById =
  297. function flockLJService_addAccountById(aAccountID, aIsTransient, aListener)
  298. {
  299.   DEBUG("{flockIWebService}.addAccountById('"+aAccountID+"', "+aIsTransient+", aListener)");
  300.   var ljAccountURN = "urn:flock:lj"+aAccountID;
  301.   var ljAccount = new this.faves_coop.Account(ljAccountURN, {
  302.     name: aAccountID,
  303.     serviceId: LIVEJOURNAL_CONTRACTID,
  304.     service: this.ljService,
  305.     accountId: aAccountID,
  306.     favicon: LIVEJOURNAL_FAVICON,
  307.     URL: "http://www.livejournal.com/portal",
  308.     isPollable: false,
  309.     isTransient: aIsTransient
  310.   });
  311.   this.account_root.children.addOnce(ljAccount);
  312.   //this.USER = ljAccount.id();
  313.   //var acct = this.getAccount(ljAccount.id());
  314.  
  315.   this.USER = ljAccountURN;
  316.   // Add the blog(s)
  317.   var ljsvc = this;
  318.   var listener = {
  319.     onResult: function(aResult) {      
  320.       ljsvc.logger.info("addAccountById: got the blog list from the server\n");
  321.       var theBlog;
  322.       while (aResult.hasMoreElements()) {
  323.         theBlog = aResult.getNext();
  324.         theBlog.QueryInterface(Components.interfaces.flockIBlogAccount);
  325.         theCoopBlog = new ljsvc.faves_coop.Blog(ljAccountURN+":"+theBlog.title, {
  326.           name: theBlog.title,
  327.           title: theBlog.title,
  328.           blogid: theBlog.blogid,
  329.           URL: theBlog.URL,
  330.           apiLink: "http://www.livejournal.com/interface/xmlrpc"
  331.         });
  332.         ljAccount.children.addOnce(theCoopBlog);
  333.       }
  334.       if (aListener) aListener.onSuccess(acct, "addAccount");
  335.     },
  336.     onError: function addAcc_listener_onError(aSubject, aStatus, aError) {
  337.       ljsvc.faves_coop.accounts_root.children.remove(ljAccount);
  338.       ljAccount.destroy();
  339.       if(aListener) {
  340.         aListener.onError(null, "ERROR", aError);
  341.       }
  342.     }
  343.   }
  344.  
  345.   this.getUsersBlogs(listener, "");
  346.  
  347.   acct = this.getAccount(ljAccountURN);
  348.   return acct;
  349. }
  350.  
  351. flockLJService.prototype.logout =
  352. function flockLJService_logout()
  353. {
  354.   this.acUtils.removeCookies(this.webDetective.getSessionCookies("livejournal"));
  355.   this.USER = null;
  356. }
  357. // END flockIWebService interface
  358.  
  359.  
  360. // BEGIN flockIManageableWebService interface
  361. flockLJService.prototype.ownsDocument =
  362. function flockLJService_ownsDocument(aDocument)
  363. {
  364.   DEBUG("{flockIManageableWebService}.ownsDocument(aDocument)");
  365.   aDocument.QueryInterface(Components.interfaces.nsIDOMHTMLDocument);
  366.   var ios = Components.classes["@mozilla.org/network/io-service;1"]
  367.                       .getService(Components.interfaces.nsIIOService);
  368.   var uri = ios.newURI(aDocument.URL, null, null);
  369.   if ((uri.host.indexOf("livejournal.com") == 0) ||
  370.       (uri.host.indexOf(".livejournal.com") > 0))
  371.   {
  372.     if (uri.host.indexOf("adserver.livejournal.com") >= 0) {
  373.       return false;
  374.     } else {
  375.       return true;
  376.     }
  377.   }
  378.   return false;
  379. }
  380.  
  381. flockLJService.prototype.updateAccountStatusFromDocument =
  382. function flockLJService_updateAccountStatusFromDocument(aDocument)
  383. {
  384.   DEBUG("{flockIManageableWebService}.updateAccountStatusFromDocument()");
  385.   if (this.webDetective.detect("livejournal", "loggedout", aDocument, null)) {
  386.     this.acUtils.markAllAccountsAsLoggedOut(LIVEJOURNAL_CONTRACTID);
  387.   } else if (this.webDetective.detect("livejournal", "loggedin", aDocument, null)) {
  388.     var results = Components.classes["@mozilla.org/hash-property-bag;1"]
  389.                             .createInstance(Components.interfaces.nsIWritablePropertyBag2);
  390.     if (this.webDetective.detect("livejournal", "accountinfo", aDocument, results)) {
  391.       var accountID = results.getPropertyAsAString("accountid");
  392.       if (accountID && accountID.length) {
  393.         var accountURN = this.acUtils.getAccountURNById(this.urn, accountID);
  394.         this.acUtils.ensureOnlyAuthenticatedAccount(accountURN);
  395.         this.USER = accountURN;
  396.       }
  397.     }
  398.   }
  399. }
  400. // END flockIManageableWebService interface
  401.  
  402.  
  403. // BEGIN flockISocialWebService interface
  404. flockLJService.prototype.decorateForPerson =
  405. function flockISocialWebService_decorateForPerson(aDocument)
  406. {
  407.   DEBUG("{flockISocialWebService}.decorateForPerson()");
  408. }
  409.  
  410. flockLJService.prototype.browseFriends =
  411. function flockLJService_browseFriends(aFriendURN, aListener)
  412. {
  413.   DEBUG("{flockISocialWebService}.browseFriends('"+aFriendURN+"')");
  414. }
  415. // END flockISocialWebService interface
  416.  
  417.  
  418. // BEGIN flockIBlogWebService interface
  419. flockLJService.prototype.parseUsersBlogs =
  420. function flockLJService_parseUsersBlogs(listener, inst)
  421. {
  422.   try {
  423.     debug("flockLJService.prototype.parseUsersBlogs\n");
  424.     var result = [];
  425.     var dom = inst._req.responseXML;
  426.     var domEntries = dom.getElementsByTagName("entry");
  427.  
  428.     if (domEntries.length > 0) { // Regular ATOM feed (ATOM)
  429.       for (var i = 0; i < domEntries.length; i++) {
  430.         debug("One entry...\n");
  431.         domEntry = domEntries[i];
  432.         title = domEntry.getElementsByTagName("title")[0].textContent;
  433.         var newAccount = new BlogAccount(title);
  434.         newAccount.api = this.shortName;
  435.         var links = domEntry.getElementsByTagName("link");
  436.         for (var j = 0; j < links.length; j++) {
  437.           var link = links[j]
  438.           switch (link.getAttribute("rel")) {
  439.             case "alternate":
  440.               newAccount.URL = link.getAttribute("href");
  441.               break;
  442.             case "http://schemas.google.com/g/2005#post":
  443.               newAccount.apiLink = link.getAttribute("href");
  444.               break;
  445.           }
  446.         }
  447.         result.push(newAccount);
  448.       }
  449.     }
  450.     else { // Just a list of links (Livejournal)
  451.       var links = dom.getElementsByTagName("link");
  452.       var result = [];
  453.       for (var i = 0; i < links.length; ++i) {
  454.         var link = links[i];
  455.         var title = link.getAttribute("title");
  456.         var rel = link.getAttribute("rel");
  457.         var href = link.getAttribute("href");
  458.         switch (rel) {
  459.           case "service.post":
  460.           {
  461.             href.match(/.+\/(.+)/);
  462.             var newAccount = new BlogAccount(title);
  463.             newAccount.api = this.shortName;
  464.             newAccount.blogid = RegExp.$1;
  465.             newAccount.apiLink = href;
  466.             newAccount.editLink = href;
  467.             result.push(newAccount);
  468.           }; break;
  469.           case "alternate":
  470.           {
  471.             for (j in result) {
  472.               if (result[j].title == title) {
  473.                 result[j].URL = href;
  474.               }
  475.             }
  476.           }; break;
  477.         }
  478.       }
  479.     }
  480.     debug("Found "+ result.length +" blogs\n");
  481.     listener.onResult(new simpleEnumerator(result));
  482.   }
  483.   catch(e) {
  484.     var logger = Components.classes["@flock.com/logger;1"]
  485.                            .getService(Components.interfaces.flockILogger);
  486.     logger.init("livejournal");
  487.     logger.error(e + " " + e.lineNumber);
  488.     listener.onError(e + " " + e.lineNumber);
  489.   }
  490. }
  491.  
  492.  
  493. flockLJService.prototype.newPost =
  494. function flockLJService_newPost(aListener, aBlogId, aPost, aPublish, aNotifications)
  495. {
  496.   var svc = this;
  497.  
  498.   var coopBlog = this.faves_coop.get(aBlogId);
  499.   var coopAccount = coopBlog.getParent();
  500.  
  501.   var pw = this.acUtils.getPassword(this.urn+':'+coopAccount.accountId);
  502.  
  503.   var extra = [];
  504.   if (aPost.extra)
  505.     while (aPost.extra.hasMore())
  506.       extra.push(aPost.extra.getNext());
  507.  
  508.   var nocomments = (extra[2] == "0") ? false : true;
  509.   var notifsArray = [];
  510.  
  511.   var date = new Date();
  512.   var hours = date.getHours();
  513.   var month = date.getMonth() + 1;
  514.   var day = date.getDate();
  515.   var year = date.getFullYear();
  516.   var hour = date.getHours();
  517.   var min = date.getMinutes();
  518.  
  519.   var labels = [];
  520.   if (aPost.tags) {
  521.     while (aPost.tags.hasMore()) {
  522.       labels.push(aPost.tags.getNext());
  523.     }
  524.   }
  525.   var content = aPost.description.replace("</lj>", "", "gi");
  526.  
  527.   var listener = {
  528.     onResult: function(aResult) {
  529.       aListener.onResult(aResult);
  530.     },
  531.     onError: function(errorcode, errormsg) {
  532.       svc.logger.error("<<<<<<<<<< Livejournal: SERVER TO FLOCK\n");
  533.       svc.logger.error("FAULT "+errorcode+" "+errormsg+"\n");
  534.  
  535.       var error = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  536.       error.serviceErrorCode = errorcode;
  537.       error.serviceErrorString = errormsg;
  538.       switch (errorcode) {
  539.         case 0: // Invalid login/pass
  540.           error.errorCode = Ci.flockIError.BLOG_INVALID_AUTH;
  541.           error.errorString = "Bad authentication";
  542.           break;
  543.         default: // Unknown error code
  544.           error.errorCode = Ci.flockIError.BLOG_UNKNOWN;
  545.           error.errorString = "Unknown Error";
  546.       }
  547.       aListener.onError(error);
  548.     },
  549.     onFault: function(errorcode, errormsg) {
  550.       svc.logger.error("<<<<<<<<<< Livejournal: SERVER TO FLOCK\n");
  551.       svc.logger.error("FAULT "+errorcode+" "+errormsg+"\n");
  552.  
  553.       var error = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  554.       error.serviceErrorCode = errorcode;
  555.       error.serviceErrorString = errormsg;
  556.       switch (errorcode) {
  557.         case 0: // Invalid login/pass
  558.           error.errorCode = Ci.flockIError.BLOG_INVALID_AUTH;
  559.           error.errorString = "Bad authentication";
  560.           break;
  561.         default: // Unknown error code
  562.           error.errorCode = Ci.flockIError.BLOG_UNKNOWN;
  563.           error.errorString = "Unknown Error";
  564.       }
  565.       aListener.onFault(error);
  566.     }
  567.   };
  568.  
  569.   var xmlrpcServer = new flockXmlRpcServer(coopBlog.apiLink);
  570.   var args = [{username: coopAccount.accountId, password: pw.password,
  571.                subject: aPost.title, event: content,
  572.                security: extra[0], allowmask: extra[1], ver: 1,
  573.                year: year, mon: month, day: day, hour: hour, min: min,
  574.                // props: [{opts_nocomments: nocomments}],
  575.                props: {taglist: labels.join(','), opt_nocomments: nocomments}
  576.                }];
  577.   xmlrpcServer.call("LJ.XMLRPC.postevent", args, listener);
  578. }
  579.  
  580. flockLJService.prototype.editPost =
  581. function flockLJService_editPost(aListener, aBlogId, aPost, aPublish, aNotifications)
  582. {
  583.   var svc = this;
  584.  
  585.   var coopBlog = this.faves_coop.get(aBlogId);
  586.   var coopAccount = coopBlog.getParent();
  587.  
  588.   var pw = this.acUtils.getPassword(this.urn+':'+coopAccount.accountId);
  589.  
  590.   var labels = [];
  591.   if (aPost.tags) {
  592.     while (aPost.tags.hasMore()) {
  593.       labels.push(aPost.tags.getNext());
  594.     }
  595.   }
  596.   var content = aPost.description.replace("</lj>", "", "gi");
  597.   var extra = [];
  598.   if (aPost.extra) {
  599.     while (aPost.extra.hasMore()) {
  600.       extra.push(aPost.extra.getNext());
  601.     }
  602.   }
  603.   var nocomments = (extra[2] == "0") ? false : true;
  604.  
  605.   var notifsArray = [];
  606.  
  607.   var listener = {
  608.     onResult: function(aResult) {
  609.       aListener.onResult(aResult);
  610.     },
  611.     onError: function(error) {
  612.       svc.logger.error("<<<<<<<<<< LiveJournal API: SERVER TO FLOCK\n");
  613.       svc.logger.error("ERROR "+error+"\n");
  614.       aListener.onError(error);
  615.     },
  616.     onFault: function(error) {
  617.       svc.logger.error("<<<<<<<<<< LiveJournal API: SERVER TO FLOCK\n");
  618.       svc.logger.error("FAULT "+error+"\n");
  619.       aListener.onFault(error);
  620.     }
  621.   };
  622.  
  623.   var xmlrpcServer = new flockXmlRpcServer(coopBlog.apiLink);
  624.   var args = [{username: coopAccount.accountId,
  625.                password: pw.password,
  626.                itemid: aPost.postid,
  627.                subject: aPost.title,
  628.                event: content,
  629.                security: extra[0],
  630.                allowmask: extra[1],
  631.                ver: 1,
  632.                props:
  633.                  {
  634.                    taglist: labels.join(','),
  635.                    opt_nocomments: nocomments
  636.                  }
  637.               }];
  638.   xmlrpcServer.call("LJ.XMLRPC.editevent", args, listener);
  639. }
  640.  
  641. flockLJService.prototype.deletePost =
  642. function flockLJService_deletePost(aListener, aBlogId, aPostid)
  643. {
  644.   debug("***** flockLJService.deletePost: not supported!\n");
  645. }
  646.  
  647. flockLJService.prototype.getPost =
  648. function flockLJService(aListener, aBlogId, aPostid)
  649. {
  650.   debug("***** flockLJService.getPost: not supported!\n");
  651. }
  652.  
  653. flockLJService.prototype.getUsersBlogs =
  654. function flockLJService_getUserBlogs(aListener, aUrl)
  655. {
  656.   var username = this.faves_coop.get(this.USER).name;
  657.   debug("USER: "+username+"\n");
  658.   var pw = this.acUtils.getPassword(this.urn+':'+username);
  659.  
  660.   var listener = new userBlogsListener(aListener);
  661.   var xmlrpcServer = new flockXmlRpcServer("http://www.livejournal.com/interface/blogger");
  662.   var args = ["flockbrowser", username, pw.password];
  663.   xmlrpcServer.call("blogger.getUsersBlogs", args, listener);
  664. }
  665.  
  666. flockLJService.prototype.getRecentPosts =
  667. function flockLJService_getRecentPosts(aListener, aBlogId, aNumber)
  668. {
  669.   var svc = this;
  670.  
  671.   var coopBlog = this.faves_coop.get(aBlogId);
  672.   var coopAccount = coopBlog.getParent();
  673.  
  674.   var pw = this.acUtils.getPassword(this.urn+':'+coopAccount.accountId);
  675.  
  676.   var listener = {
  677.     onResult: function(aResult) {
  678.       var result = [];
  679.       svc.logger.info("<<<<<<<<<< LiveJournal API: SERVER TO FLOCK\n");
  680.       for (i=0; i<aResult.length; i++) {
  681.         var post = new BlogPost();
  682.         var content = aResult[i].content;
  683.         if (content.match(/<title.*>(.+?)</)) {
  684.           post.title = RegExp.$1;
  685.         }
  686.         post.issued = aResult[i].dateCreated.getTime();
  687.         post.postid = aResult[i].postId.split(":")[1];
  688.         result.push(post);
  689.       }
  690.  
  691.       // and return the data source through the listener
  692.       aListener.onResult(new simpleEnumerator(result));
  693.     },
  694.     onError: function(error) {
  695.       svc.logger.error("<<<<<<<<<< LiveJournal API: SERVER TO FLOCK\n");
  696.       svc.logger.error("ERROR "+error+"\n");
  697.       aListener.onError(error);
  698.     },
  699.     onFault: function(code, error) {
  700.       svc.logger.error("<<<<<<<<<< LiveJournal API: SERVER TO FLOCK\n");
  701.       svc.logger.error("FAULT #"+code+": "+error+"\n");
  702.       var flerror = Cc['@flock.com/error;1'].createInstance(Ci.flockIError);
  703.       flerror.serviceErrorCode = code;
  704.       flerror.serviceErrorString = error;
  705.       flerror.errorCode = Ci.flockIError.BLOG_UNKNOWN_ERROR;
  706.       flerror.errorString = "Unknown Error";
  707.  
  708.       aListener.onFault(flerror);// TODO: bubble up the error to the UI
  709.     }
  710.   }
  711.  
  712.   var xmlrpcServer = new flockXmlRpcServer("http://www.livejournal.com/interface/blogger");
  713.   // The username is used as the blogid for this call
  714.   var args = ['flockblog', coopAccount.accountId, coopAccount.accountId, pw.password, aNumber];
  715.   xmlrpcServer.call("blogger.getRecentPosts", args, listener);
  716. }
  717.  
  718. flockLJService.prototype.getCategoryList =
  719. function flockLJService_getCategoryList(aListener, aAccount)
  720. {
  721.   aListener.onResult(new simpleEnumerator([]));
  722. }
  723.  
  724. flockLJService.prototype.init =
  725. function flockLJService_init()
  726. {
  727.   DEBUG(".init()");
  728.   if (this.initialized) {
  729.     return;
  730.   }
  731.  
  732.   var evtID = this._profiler.profileEventStart("lj-init");
  733.  
  734.   this.prefService = Components.classes["@mozilla.org/preferences-service;1"]
  735.                                .getService(Components.interfaces.nsIPrefBranch);
  736.   if ( this.prefService.getPrefType(SERVICE_ENABLED_PREF) &&
  737.        !this.prefService.getBoolPref(SERVICE_ENABLED_PREF) )
  738.   {
  739.     DEBUG("Pref "+SERVICE_ENABLED_PREF+" set to FALSE... not initializing.");
  740.     var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
  741.     catMgr.deleteCategoryEntry("wsm-startup", CATEGORY_COMPONENT_NAME, true); 
  742.     catMgr.deleteCategoryEntry("flockWebService", CATEGORY_ENTRY_NAME, true); 
  743.     return;
  744.   }
  745.  
  746.   // Logger
  747.   this.logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  748.   this.logger.init("livejournal");
  749.  
  750.   this.faves = RDFS.GetDataSourceBlocking('rdf:flock-favorites');
  751.   this.faves_coop = Cc['@flock.com/singleton;1'].getService(Ci.flockISingleton).getSingleton('chrome://browser/content/flock/common/load-faves-coop.js').wrappedJSObject;
  752.   this.account_root = this.faves_coop.accounts_root;
  753.  
  754.   this.ljService = new this.faves_coop.Service(
  755.   'urn:livejournal:service', {
  756.     name: 'livejournal',
  757.     desc: 'The LiveJournal Service'
  758.   });
  759.   this.ljService.serviceId = LIVEJOURNAL_CONTRACTID;
  760.  
  761.   var ljHomepage = new this.faves_coop.Favorite(
  762.   'urn:livejournal:actions:homepage', {
  763.     name: 'LiveJournal.com',
  764.   });
  765.  
  766.   var ljOpenProfile = new this.faves_coop.Action(
  767.   'urn:livejournal:actions:openprofile', {
  768.     name: 'Open Profile',
  769.     method: 'openProfile',
  770.     service: LIVEJOURNAL_CONTRACTID,
  771.     flavour: 'view',
  772.   });
  773.  
  774.   var ljOpenBlog = new this.faves_coop.Action(
  775.     'urn:livejournal:actions:openblog', {
  776.       name: 'Open Blog',
  777.       method: 'openBlog',
  778.       service: LIVEJOURNAL_CONTRACTID,
  779.       flavour: 'view',
  780.   });
  781.  
  782.   var ljViewPhotos = new this.faves_coop.Action(
  783.    'urn:livejournal:actions:openphotos', {
  784.       name: 'View Pictures',
  785.       method: 'openPhotos',
  786.       service: LIVEJOURNAL_CONTRACTID,
  787.       flavour: 'view',
  788.   });
  789.   /*
  790.   var ljViewFriends = new this.faves_coop.Action(
  791.     'urn:livejournal:actions:viewfriends', {
  792.       name: 'View Friends',
  793.       method: 'viewFriends',
  794.       service: LIVEJOURNAL_CONTRACTID,
  795.       flavour: 'view',
  796.   });
  797. */
  798.   var ljPostComment = new this.faves_coop.Action(
  799.   'urn:livejournal:actions:postcomment', {
  800.     name: 'Post Comment',
  801.     method: 'postComment',
  802.     service: LIVEJOURNAL_CONTRACTID,
  803.     flavour: 'contact',
  804.   });
  805.  
  806.   var ljShare = new this.faves_coop.Action(
  807.   'urn:livejournal:actions:share', {
  808.     name: 'Share via Livejournal',
  809.     method: 'shareViaLJ',
  810.     service: LIVEJOURNAL_CONTRACTID,
  811.     flavour: 'share',
  812.   });
  813.  
  814.   var aimChat = new this.faves_coop.Action(
  815.     'urn:livejournal:actions:aimchat', {
  816.       name: 'Chat via Aim',
  817.       method: 'chatViaAim',
  818.       service: LIVEJOURNAL_CONTRACTID,
  819.       flavour: 'contact',
  820.   });
  821.  
  822.   var aimShare = new this.faves_coop.Action(
  823.     'urn:livejournal:actions:aimshare', {
  824.       name: 'Share via Aim',
  825.       method: 'shareViaAim',
  826.       service: LIVEJOURNAL_CONTRACTID,
  827.       flavour: 'share',
  828.   });
  829.  
  830.   var ljEditProfile = new this.faves_coop.Action(
  831.     'urn:livejournal:actions:editprofile', {
  832.      name: 'Edit Profile',
  833.      method: 'editProfile',
  834.      service: LIVEJOURNAL_CONTRACTID,
  835.      flavour: 'accountaction',
  836.   });
  837.  
  838.   var ljViewFriends = new this.faves_coop.Action(
  839.   "urn:livejournal:actions:viewFriends", {
  840.     name: "View Friends",
  841.     method: "viewFriends",
  842.     service: LIVEJOURNAL_CONTRACTID,
  843.     flavour: 'view',
  844.   });
  845.  
  846.   this.ljService.children.addOnce(ljViewFriends);
  847.   this.ljService.children.addOnce(ljHomepage);
  848.   this.ljService.children.addOnce(aimShare);
  849.   this.ljService.children.addOnce(aimChat);
  850.   this.ljService.children.addOnce(ljOpenProfile);
  851.   this.ljService.children.addOnce(ljOpenBlog);
  852.   this.ljService.children.addOnce(ljViewPhotos);
  853.   this.ljService.children.addOnce(ljPostComment);
  854.   this.ljService.children.addOnce(ljShare);
  855.  
  856.   // Load Web Detective file
  857.   this.webDetective = this.acUtils.useWebDetective("livejournal.xml");
  858.   for (var s in gStrings) {
  859.     gStrings[s] = this.webDetective.getString("livejournal", s, gStrings[s]);
  860.   }
  861.   this.ljService.domains = gStrings["domains"];
  862.   this.ljService.loginURL = gStrings["userlogin"];
  863.   ljHomepage.URL = gStrings["homepage"];
  864.  
  865.   this.urn = this.ljService.id();
  866.  
  867.   // Update auth states
  868.   if (this.webDetective.detectCookies("livejournal", "loggedout", null)) {
  869.     this.acUtils.markAllAccountsAsLoggedOut(LIVEJOURNAL_CONTRACTID);
  870.   }
  871.  
  872.   // Iterate all the ljAccounts, make sure their actions are correct
  873.   var accounts = this.faves_coop.Account.find({serviceId: LIVEJOURNAL_CONTRACTID});
  874.   if (accounts.length) {
  875.     this.USER = accounts[0].accountId;
  876.     for (var i = 0; i < accounts.length; i++) {
  877.       this.updateActions(accounts[i].id());
  878.     }
  879.   }
  880.  
  881.   this.initialized = true;
  882.  
  883.   this._profiler.profileEventEnd(evtID, "");
  884. }
  885.  
  886. // END flockIBlogWebService interface
  887.  
  888. flockLJService.prototype.refresh =
  889. function flockLJService_refresh(aURN, aListener)
  890. {
  891.   //DEBUG("flockLJService refresh with aURN of "+aURN);
  892.  
  893.   var refreshItem = this.faves_coop.get(aURN);
  894.  
  895.   if (refreshItem instanceof this.faves_coop.Favorite) {
  896.     this.refreshItem(aURN, aListener);
  897.   } else {
  898.     //throw Components.results.NS_ERROR_ABORT;
  899.   }
  900. }
  901.  
  902. flockLJService.prototype.refreshItem =
  903. function flockLJService_refreshItem(aURN, aListener)
  904. {
  905.   throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  906. }
  907.  
  908. flockLJService.prototype.refreshAccount =
  909. function flockLJService_refreshAccount(aURN, aListener)
  910. {
  911.   DEBUG("LJService - refreshAccount with aURN of " + aURN);
  912.   if (!aURN) return;
  913.  
  914.   var refreshItem = this.faves_coop.get(aURN);
  915.  
  916.   var foafurl = gStrings["userfoaf"].replace("%accountid%", refreshItem.accountId);
  917.   var inst = this;
  918.   var refreshAcctListener = {
  919.     onResult: function(ds) {
  920.       var foaf_coop = new Coop(ds, COOP_FOAFTYPES_CHROME_URL);
  921.       var user = inst.faves_coop.get(aURN);
  922.       DEBUG("LJService - gonna do find for "+user.accountId);
  923.       var p = foaf_coop.FoafPerson.find({nick: user.accountId})[0];
  924.       var e = p.knows.enumerate();
  925.       var lister = this;
  926.       function myWorker(shouldYield) {
  927.         var count = 0;
  928.         while (e.hasMoreElements()) {
  929.           if (lister.cancel) return;
  930.           count++;
  931.           var friend = e.getNext();
  932.           inst.addCoopPerson(friend, false, aURN);
  933.           if (count % 3 == 0 && shouldYield()) yield;
  934.         }
  935.         if (aListener) aListener.onResult();
  936.       }
  937.       getCompTK().schedule(gTimers, 0.4, 40, myWorker);
  938.     },
  939.     cancelMe: function() {
  940.       this.cancel = true;
  941.     },
  942.   };
  943.  
  944.   // get a datasource for holding the foaf
  945.   this.loadFoaf(foafurl, refreshAcctListener);
  946. }
  947.  
  948.  
  949. flockLJService.prototype.getUserAvatar =
  950. function flockLJService_getUserAvatar(aURN)
  951. {
  952.   var refreshItem = this.faves_coop.get(aURN);
  953.   var url = makeLiveJournalURL('userpics', refreshItem.accountId);
  954.  
  955.   var hr = Components.classes['@mozilla.org/xmlextras/xmlhttprequest;1']
  956.                      .createInstance(Components.interfaces.nsIXMLHttpRequest);
  957.   var ljServ = this;
  958.  
  959.   var onReadyStateFunc = function(eEvt) {
  960.     if(hr.readyState == 4) {
  961.       if (hr.responseText.match(/<img src='http:\/\/www\.livejournal\.com\/userpic\/(.+?)'/)) {
  962.         refreshItem.avatar ="http://www.livejournal.com/userpic/" + RegExp.$1;
  963.         var myparent = refreshItem.getParent();
  964.         if (myparent) myparent.avatar = refreshItem.avatar;
  965.       }
  966.     }
  967.   };
  968.  
  969.   hr.onreadystatechange = onReadyStateFunc;
  970.   hr.detachLoadGroup = true;
  971.   hr.open('GET', url,true);
  972.   hr.overrideMimeType("text/txt");
  973.   hr.send(null);
  974. }
  975.  
  976.  
  977. flockLJService.prototype.addCoopPerson =
  978. function flockLJService_addCoopPerson(aFoafFriend, bIsTransient, aFriendOfURN)
  979. {
  980. }
  981.  
  982. flockLJService.prototype.addFoafPerson =
  983. function flockLJService_addFoafPerson(aFoafFriend, aFriendOfURN)
  984. {
  985. }
  986.  
  987.  
  988. flockLJService.prototype.refreshIdentity =
  989. function flockLJService_refreshIdentity(aURN, aListener)
  990. {
  991. }
  992.  
  993. flockLJService.prototype.refreshIdentityCB =
  994. function flockLJService_refreshIdentityCB(aURN, ds)
  995. {
  996. }
  997.  
  998. flockLJService.prototype.doAction =
  999. function flockLJService_doAction(aMethodName, aURN, aSubject)
  1000. {
  1001. }
  1002.  
  1003. flockLJService.prototype.updateActions =
  1004. function flockLJService_updateActions(aURN)
  1005. {
  1006.   var serviceActions = this.ljService.children.enumerate();
  1007.   var coopObj = this.faves_coop.get(aURN);
  1008.   coopObj.enabledAction.removeAll();
  1009.   if (coopObj.isInstanceOf(this.faves_coop.Account)) {
  1010.     while (serviceActions.hasMoreElements()) {
  1011.       var action = serviceActions.getNext();
  1012.       if (action.flavour == "accountaction" ||
  1013.           action.flavour == "accountview" ||
  1014.           action.flavour == "view")
  1015.         coopObj.enabledAction.add(action);
  1016.     }
  1017.   }
  1018. }
  1019.  
  1020. // ========== END flockLJService class ==========
  1021.  
  1022.  
  1023.  
  1024.  
  1025. // ========== BEGIN XPCOM Module support ==========
  1026.  
  1027. function createModule(aParams) {
  1028.   return {
  1029.     registerSelf: function (aCompMgr, aFileSpec, aLocation, aType) {
  1030.       var aCompMgr = aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
  1031.       aCompMgr.registerFactoryLocation( aParams.CID, aParams.componentName,
  1032.                                         aParams.contractID, aFileSpec,
  1033.                                         aLocation, aType );
  1034.       var catMgr = Cc["@mozilla.org/categorymanager;1"]
  1035.         .getService(Ci.nsICategoryManager);
  1036.       if (!aParams.categories) { aParams.categories = []; }
  1037.       for (var i = 0; i < aParams.categories.length; i++) {
  1038.         var cat = aParams.categories[i];
  1039.         catMgr.addCategoryEntry( cat.category, cat.entry,
  1040.                                  cat.value, true, true );
  1041.       }
  1042.     },
  1043.     getClassObject: function (aCompMgr, aCID, aIID) {
  1044.       if (!aCID.equals(aParams.CID)) { throw Cr.NS_ERROR_NO_INTERFACE; }
  1045.       if (!aIID.equals(Ci.nsIFactory)) { throw Cr.NS_ERROR_NOT_IMPLEMENTED; }
  1046.       return { // Factory
  1047.         createInstance: function (aOuter, aIID) {
  1048.           if (aOuter != null) { throw Cr.NS_ERROR_NO_AGGREGATION; }
  1049.           var comp = new aParams.componentClass();
  1050.           if (aParams.implementationFunc) { aParams.implementationFunc(comp); }
  1051.           return comp.QueryInterface(aIID);
  1052.         }
  1053.       };
  1054.     },
  1055.     canUnload: function (aCompMgr) { return true; }
  1056.   };
  1057. }
  1058.  
  1059. // NS Module entrypoint
  1060. function NSGetModule(aCompMgr, aFileSpec) {
  1061.   return createModule({
  1062.     componentClass: flockLJService,
  1063.     CID: LIVEJOURNAL_CID,
  1064.     contractID: LIVEJOURNAL_CONTRACTID,
  1065.     componentName: CATEGORY_COMPONENT_NAME,
  1066.     implementationFunc: function (aComp) { getCompTK().addAllInterfaces(aComp); },
  1067.     categories: [
  1068.       { category: "wsm-startup", entry: CATEGORY_COMPONENT_NAME, value: LIVEJOURNAL_CONTRACTID },
  1069.       { category: "flockWebService", entry: CATEGORY_ENTRY_NAME, value: LIVEJOURNAL_CONTRACTID }
  1070.     ]
  1071.   });
  1072. }
  1073.  
  1074. // ========== END XPCOM module support ==========
  1075.  
  1076.  
  1077.  
  1078. // ================================================
  1079. // ========== BEGIN flockLJAccount class ==========
  1080. // ================================================
  1081.  
  1082. function flockLJAccount()
  1083. {
  1084.   this.acUtils = Components.classes["@flock.com/account-utils;1"]
  1085.                            .getService(Components.interfaces.flockIAccountUtils);
  1086.   this.service = Components.classes[LIVEJOURNAL_CONTRACTID]
  1087.                            .getService(Components.interfaces.flockIWebService);
  1088.   this.faves = RDFS.GetDataSourceBlocking("rdf:flock-favorites");
  1089.   this._coop = Components.classes['@flock.com/singleton;1']
  1090.                          .getService(Components.interfaces.flockISingleton)
  1091.                          .getSingleton('chrome://browser/content/flock/common/load-faves-coop.js')
  1092.                          .wrappedJSObject;
  1093.   this._ctk = {
  1094.     interfaces: [
  1095.       "nsISupports",
  1096.       "flockIWebServiceAccount",
  1097.       "flockIBlogWebServiceAccount",
  1098.       "flockISocialWebServiceAccount",
  1099.     ],
  1100.   };
  1101.   getCompTK().addAllInterfaces(this);
  1102. }
  1103.  
  1104. // BEGIN flockIWebServiceAccount interface
  1105. flockLJAccount.prototype.activate =
  1106. function flockLJAccount_activate(aListener)
  1107. {
  1108.   DEBUG("{flockIWebServiceAccount}.activate()");
  1109.   var acctCoopObj = this._coop.get(this.urn);
  1110.   acctCoopObj.isPollable = true;
  1111.   this.acUtils.ensureOnlyAuthenticatedAccount(this.urn);
  1112. }
  1113. // END flockIWebServiceAccount interface
  1114.  
  1115.  
  1116. // BEGIN flockIBlogWebServiceAccount interface
  1117. flockLJAccount.prototype.getBlogs =
  1118. function flockLJAccount_getBlogs()
  1119. {
  1120.   DEBUG("{flockIBlogWebServiceAccount}.getBlogs()");
  1121.   var blogsEnum = {
  1122.     QueryInterface: function(iid) {
  1123.       if (!iid.equals(Components.interfaces.nsISupports) &&
  1124.           !iid.equals(Components.interfaces.nsISimpleEnumerator))
  1125.       {
  1126.         throw Components.results.NS_ERROR_NO_INTERFACE;
  1127.       }
  1128.       return this;
  1129.     },
  1130.     hasMoreElements: function() {
  1131.       return false;
  1132.     },
  1133.     getNext: function() {
  1134.     }
  1135.   };
  1136.   return blogsEnum;
  1137. }
  1138. // END flockIBlogWebServiceAccount interface
  1139.  
  1140.  
  1141. // BEGIN flockISocialWebServiceAccount interface
  1142. flockLJAccount.prototype.browseFriends =
  1143. function flockLJAccount_browseFriends(aFriendURN, aListener)
  1144. {
  1145.   DEBUG("{flockISocialWebServiceAccount}.browseFriends('"+aFriendURN+"')");
  1146.   this.service.browseFriends(aFriendURN, aListener);
  1147. }
  1148. // END flockISocialWebServiceAccount interface
  1149.  
  1150. // ========== END flockLJAccount class ==========
  1151.